//24c02.cpp
#include "24c02.h"
#include <Wire.h>

/**
 * @brief 初始化AT24C02 EEPROM
 * @note  配置I2C引脚和通信频率
 * @param 无
 * @retval 无
 */
void at24c02_init()
{
    //Wire-IIC0 可使用Wire1-IIC1
    Wire.begin(IIC_SDA, IIC_SCL,400000);    //24c02手册描述为400KHz
}

/**
 * @brief  向指定地址写入一个字节数据
 * @param  addr: 数据地址 (0~255)
 * @param  data: 要写入的数据
 * @retval 无
 */
void at24c02_write_one_byte(uint8_t addr, uint8_t data)
{
    Wire.beginTransmission(EEPROM_ADDR);    /*发送从机的7位原件地址到发送队列*/
    Wire.write(addr);     /*发送要写人从机数据的地址到发送队列*/
    Wire.write(data);     /*发送要写人从机数据到发送队列*/
    Wire.endTransmission(1);    //发送停止信号结束传输

    delay(10);  //给EEPROM写数据一个时间间隔
}



/**
  * @brief  读取一个字节数据
  * @param  addr 数据地址
  *     addr: 0~255
  * @retval 读取到的字节，错误会返回0xFF
  */
uint8_t at24c02_read_one_byte(uint8_t addr)
{
    Wire.beginTransmission(EEPROM_ADDR);    /*发送从机的7位原件地址到发送队列*/
    Wire.write(addr);     /*发送要读取从机数据的地址到发送队列*/
    Wire.endTransmission(0);    //重新发一个start信号,保持连接

    Wire.requestFrom(EEPROM_ADDR, 1);   /*获取一个字节到缓冲区*/
    if(Wire.available() != 0)   //如果缓冲区长度不为零
    {
        return Wire.read();
    }

    return 0xFF;
}

/**
 * @brief  向指定地址开始写入指定长度的数据
 * @param  addr: 数据起始地址 (0~255)
 * @param  data: 要写入的数据指针
 * @param  len:  要写入的数据长度
 * @retval 无
 * @note   AT24C02每页8字节，写入时需要考虑页边界
 */
void at24c02_write_bytes(uint8_t addr,const char *data, uint8_t len)
{
    uint8_t written = 0;
    
    while (written < len) {
        // 计算当前页剩余空间（AT24C02每页8字节）
        uint8_t page_remain = 8 - (addr % 8);
        // 计算本次写入的字节数
        uint8_t write_len = (len - written) < page_remain ? (len - written) : page_remain;
        
        Wire.beginTransmission(EEPROM_ADDR);
        Wire.write(addr);  // 发送要写入数据的地址
        
        // 写入数据
        for(uint8_t i = 0; i < write_len; i++) {
            Wire.write(data[written + i]);
        }
        //这里拉高为1,读操作
        Wire.endTransmission(1);  // 发送停止信号,拉高释放,结束传输
        delay(5 * write_len);    // 根据写入字节数给予足够延时
        
        // 更新地址和已写字节数
        addr += write_len;
        written += write_len;
    }
}



/**
 * @brief  从指定地址开始读取指定长度的数据
 * @param  addr: 数据起始地址 (0~255)
 * @param  pbuf: 用于存储读取数据的缓冲区指针
 * @param  len:  要读取的数据长度
 * @retval 无
 * @note   限制每次读取不超过64字节以避免超出Wire库缓冲区
 */
void at24c02_read_bytes(uint8_t addr, uint8_t *pbuf, uint8_t len)
{
    uint8_t readed = 0;
    
    while (readed < len) {
        // 限制每次读取的字节数，避免超出Wire库缓冲区（通常为128字节）
        uint8_t read_len = len - readed;
        if (read_len > 64) {  // 保守起见，限制为64字节
            read_len = 64;
        }
        
        Wire.beginTransmission(EEPROM_ADDR);    /*发送从机的7位原件地址到发送队列*/
        Wire.write(addr);   /*发送要读取从机数据的地址到发送队列*/
        //这里拉低为0,写操作
        Wire.endTransmission(false);  // 重新发一个start信号,拉低SDA,保持连接

        Wire.requestFrom((uint8_t)EEPROM_ADDR, (uint8_t)read_len);
        
        // 使用获取实际可读取的字节数
        uint8_t available = Wire.available();

        //缓冲区中没有字节,跳出循环
        if (available == 0) {
            break;
        }

        for(uint8_t i = 0; i < available && (readed + i) < len; i++) {
            pbuf[readed + i] = Wire.read();
        }
        
        //更新地址和已读字节数
        addr += available;
        readed += available;
    }
}

/**
 * @brief       检查AT24C02是否正常
 * @note        检测原理: 在器件的末地址写如0X55, 然后再读取, 如果读取值为0X55
 *              则表示检测正常. 否则,则表示检测失败.
 * @param       无
 * @retval      检测结果
 *              true: 检测成功
 *              false: 检测失败
 */
bool at24c02_check(void)
{
    uint8_t temp;
    temp = at24c02_read_one_byte(255);      /* 避免每次开机都写AT24CXX */
    if (temp == 0X55)                       /* 读取数据正常 */
    {
        return true;
    }

    /* 排除第一次初始化的情况 */
    at24c02_write_one_byte(255, 0X55);  /* 先写入数据 */
    temp = at24c02_read_one_byte(255);  /* 再读取数据 */
    if (temp == 0X55) 
    {
        return true;
    }

    return false;
}